DevTool Demo Virtual IMU
This demo is a comprehensive example of how to implement custom development within SOLIS using the MAX Development Tool. It walks through step-by-step instructions for creating a new custom component that could be used as a replacement for a broken IMU.
Overview
SOLIS is built with a modular and flexible design. When combined with the MAX Development Tool, SOLIS supports customization by running MAX Flight Software with ODySSy to generate simulation data and then using the MAX Development Tool to rapidly and reliably write code to run within MAX.
This tutorial will cover
- Fundamental concepts of how SOLIS, MAX, and the MAX DevTool interact.
- How to use the MAX DevTool to get custom logic available in MAX.
- How to configure SOLIS to create, connect, and configure the custom logic.
- How to run the custom logic, get telemetry from it, and send commands to it.
To complete this tutorial, you will need
- STK SOLIS 12 (or later)
- Visual Studio 2017 (or later) with SDK installed
- Basic knowledge of C++
Demonstration Example & Development Process
Tutorial Example
- An IMU is malfunctioning, and you’ve been tasked with developing an algorithm to replace this sensor dependency.
- You wish to develop and test this within SOLIS.
Tutorial Notes
- The purpose of this tutorial is to demonstrate the concepts that are required to develop custom logic; not to arrive at a fully-robust algorithm to meet the objective.
- With this knowledge, you will be well on your way to solving any customization idea.
- SOLIS and MAX are very valuable tools for developing such an algorithm, due to the rapid cycles of design, implementation, and testing.
- This is true regardless of the final Flight Software chosen to implement within.
The Custom Development Process
Creating a Project
Demo Directories
A completed instance of this demo is provided in the delivered SOLIS Example Scenarios
- Distributed via the SOLIS 12.0 installer
C:\ProgramData\AGI\STK 12\SOLIS\ExampleScenarios\SOLISExample_DevToolDemo_Virtual_IMU
To begin a new instance of this demo
- Make the new directory:
C:\ProgramData\AGI\STK 12\SOLIS\DevToolDemo_Virtual_IMU
- Copy the contents of:
C:\ProgramData\AGI\STK 12\SOLIS\ExampleScenarios\SOLISExample_DevToolDemo_Virtual_IMU\_FreshDemoContent
intoC:\ProgramData\AGI\STK 12\SOLIS\DevToolDemo_Virtual_IMU
- Open STK Scenario:
C:\ProgramData\AGI\STK 12\SOLIS\DevToolDemo_Virtual_IMU\SOLISExample_Landmapper_BC.sc
- Open SOLIS on the Landmapper_BC satellite within this scenario
The DevTool will rapidly and reliably write code to run within MAX.
Open the DevTool
In SOLIS click the button to get started. Default values are all that is needed for this project.
Save setting to RunDevTool_Virtual_IMU.bat
file for easy running of the DevTool next time.
Run the DevTool
Click on the Run DevTool button to run the DevTool. This will generate a Visual Studio solution, ready for your custom development.
.bat
is equivalent to clicking the Run DevTool button in the UI.Build Project and Refresh SOLIS
The solution will build successfully “out of the box"
After building, hit on the SOLIS GUI and you will see the DLL with a timestamp. This is an indication that SOLIS is linked with the Virtual_IMU.
Step 1: Conceptual Design
- With a potentially bad IMU, we’d like to have the ability to remove this dependency in the system.
- We will create a new block called Virtual_IMU.
- By having this block produce the same interface as IMU Processing, we can simply connect the output of Virtual IMU to the Attitude Determination.
- We will also connect the original IMU Processing interface to the Virtual IMU, so that we can continue to pass that signal to Attitude Determination (if desired).
- Virtual_IMU will get attitude quaternion data from the Star Tracker. This information can be “back-differenced” to form an estimate of spacecraft rates.
- We know this quaternion information is going into the Attitude Determination (Kalman Filter), so we will utilize that same interface to get the data into Virtual_IMU.
- We will build some commands, telemetry, and parameters into Virtual_IMU to show those concepts.
Step 2: Translation of design to XML
With the concept identified; it’s time to investigate how it fits with the existing system. To determine where to start, we will look at the MAX ADCS_Sensors.xml
found in C:\Program Files\AGI\STK 12\Solis\ExampleMAXDevToolXML
to find what interface the IMU Processing produces.
Finding the IMU Interface
A search for “IMU” reveals the FSW_SenProc_IMU interface.
- This interface embeds the FSW_SenProc_RTE interface, which we see contains a VECTOR of
Rate_BDY_rdps
.
To confirm FSW_SenProc_RTE is what we want, a search for “Determination” in ADCS_FSW.xml
shows this is the connection to the AD.
Adding the RTE Interface
Recalling from our conceptual design, we’d like our new Virtual_IMU to produce this RTE interface. This is done using the <BaseInterface Name="FSW_SenProc_RTE"/>
tag.
<?xml version="1.0" encoding="utf-8"?>
<Extension xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="MAXDevToolSchema.xsd" xmlns="asi_mc">
<SettingsDefinition>
<FileHeader>
============================================================================
A custom algorithm demonstration
============================================================================
</FileHeader>
</SettingsDefinition>
<ComponentDefinition Name="Virtual_IMU">
<BaseInterface Name="FSW_SenProc_RTE"/>
</ComponentDefinition>
</Extension>
Run the DevTool using the RunDevTool_Virtual_IMU.bat
You should now see new files in your Virtual_IMU solution. Around line 141 within Virtual_IMU.cpp
you will see the return function that will provide rate data across our FSW_SenProc_RTE interface.
Bringing Star Tracker Data into the Virtual IMU
Using the search concepts from adding the RTE Interface, we bring Star Tracker Data into the Virtual IMU using the <Configuration><Interface>
Tag in the XML.
Write the XML shown below and run the RunDevTool_Virtual_IMU.bat
Start of file:
<?xml version="1.0" encoding="utf-8"?>
<Extension xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="MAXDevToolSchema.xsd" xmlns="asi_mc">
<SettingsDefinition>
<FileHeader>
============================================================================
A custom algorithm demonstration
============================================================================
</FileHeader>
</SettingsDefinition>
<ComponentDefinition Name="Virtual_IMU">
<BaseInterface Name="FSW_SenProc_RTE"/>
Add <Configuration>
and <Interface>
:
<Configuration>
<Interface InterfaceType="FSW_SenProc_ADSensor" Description="Pull in the CBI2BDY quaternion from the Star Tracker"/>
</Configuration>
Rest of the file:
</ComponentDefinition>
</Extension>
Access to our Star Tracker data within the Virtual IMU
Within Virtual_IMU.cpp
, you should now be able to access the m_Connection
structure.
Write the following two lines, providing access to our Star Tracker data within the Virtual IMU component
bool str_Valid = m_Connection.FSW_SenProc_ADSensor_ptr->GetIsValid();
QUATERNION str_QCBI2BDY = m_Connection.FSW_SenProc_ADSensor_ptr->GetSensedQuaternion_CBI2BDT();
Full XML Translation
Example XML
<?xml version="1.0" encoding="utf-8"?>
<Extension xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="MAXDevToolSchema.xsd" xmlns="asi_mc">
<SettingsDefinition>
<FileHeader>
============================================================================
A custom algorithm demonstration
============================================================================
</FileHeader>
</SettingsDefinition>
<ComponentDefinition Name="Virtual_IMU">
<BaseInterface Name="FSW_SenProc_RTE"/>
<!-- A local definition of an enumeration that will be used in parameters, commands, and telemetry -->
<DefinedEnum Name="ERateSource">
<Enum Value="0" Text="IMU"/>
<Enum Value="1" Text="Virtual_IMU"/>
</DefinedEnum>
<!-- This section represents all things that are set from configuration files -->
<Configuration>
<Interface InterfaceType="FSW_SenProc_ADSensor" Description="Pull in the CBI2BDY quaternion from the Star Tracker"/>
<Interface InterfaceType="FSW_SenProc_RTE" Description="Pull in the original IMU data"/>
<EnumParameter Name="E_InitialRateSource" Enum="ERateSource" DefaultValue="0" Description="Initial rate source to use"/>
<BasicParameter Name="D_MaxDeltaTimeForRate_s" Type="double" Units="sec" MinValue="0.0" DefaultValue="10.0" Description="Maximum time between STR measurements to compute the back-difference"/>
</Configuration>
<Commands>
<Command Mnemonic="SETSOURCE" Description="Set the source of data to pass through">
<ComponentEnumArgument Name="Source" DefinedEnum="ERateSource" Description="Which source to pass through"/>
</Command>
</Commands>
<Telemetry>
<ComponentEnumItem Name="RateSource" DefinedEnum="ERateSource" Units="enum" Description="The currently selected rate source"/>
<!-- Note: All telemetry is a single channel. The [X:Y:Z] syntax below is a macro to form 3 telemetry mnemonics (doubles) with one line of XML on the VECTOR-->
<BasicItem Name="Rate_BDY[X:Y:Z]" LocalName="Rate_BDY_rdps" Type="VECTOR" Units="rad/s" Description="The computed virtual rate in the BDY [X:Y:Z] frame"/>
</Telemetry>
</ComponentDefinition>
</Extension>
XML Schema Used
- Extension The root-level XML node, containing any number of component interface and/or definition descriptions.
- SettingsDefinition Optional settings
- ComponentDefinition A Core building block of adding software to augment MAX.
- BaseInterface Defines the interface(s) that the Component will produce. In this case, we are producing the FSW_SenProc_RTE interface which will be used to send rate data to Attitude Determination.
- DefinedEnum Defines an enumeration local to this component. We will utilize this enumeration within a parameter, command, and telemetry, as we switch the source of the rate signal output.
- Configuration
- Interface Defines the interface(s) that this component needs to get data from. In this case, we are getting data from the IMU and the Star Tracker.
- EnumParameter & BasicParameter Used to create parameters into the system that are defined within the Configuration files (*.cfg).
- Commands Defines the commands. In this case, we have one command that has an input argument to change the rate source.
- Telemetry Defines the telemetry data (to STK Data Provider and/or to CSV) from this component.
Step 3: Implement logic in the Virtual IMU
Now that the DevTool has written all the framework, it’s time to start implementing logic.
Basics First
Virtual_IMU.hpp
We will need to define a couple variables, in the “private” section.
double m_PrevTime_s;
QUATERNION m_PrevQ_CBI2BDY;
Virtual_IMU.cpp
Initialize the new variables.
void CVirtual_IMU::CommonConstructor(void)
{
m_PrevQ_CBI2BDY = C_QId;
m_PrevTime_s = 0.0;
}
At the bottom of Init() – Initialize the RateSource telemetry to the initial parameter.
m_Tlm.RateSource = m_Parm.E_InitialRateSource;
Within GetRate_BDY_rdps() – Return the computed rate across the FSW_SenProc_RTE interface.
const VECTOR CVirtual_IMU::GetRate_BDY_rdps(void)
{
return m_Tlm.Rate_BDY_rdps;
}
Within GetMeasRateValid() – Return rate always valid (simplified assumption for now)
Hardware::E_SenMeasValidity CVirtual_IMU::GetMeasRateValid(void)
{
return Hardware::E_ValidThisFrame;
}
Within AcceptCmd_SETSOURCE() – Update the RateSource to the command argument
E_FSW_STATUS CVirtual_IMU::AcceptCmd_SETSOURCE(const SETSOURCE_CMD_STRUCT* arguments )
{
m_Tlm.RateSource = arguments->Source;
return E_FSW_OK;
}
The primary algorithm will be implemented in the ProcessInputs()
function
Primary Algorithm
int CVirtual_IMU::ProcessInputs(void)
{
int Ret = 0;
// Setup a switch statement that either passes the raw IMU signal through, or computes the rate from the Star Tracker.
switch (m_Tlm.RateSource)
{
case ERateSource::E_IMU:
// In this case, just pass through what the IMU reported
m_Tlm.Rate_BDY_rdps = m_Connection.FSW_SenProc_RTE_ptr->GetRate_BDY_rdps();
break;
case ERateSource::E_Virtual_IMU:
// This is where we will perform calculations to form a rate estimate from the Star Tracker
// If the Star Tracker is valid, get the data from it to use for upcoming calculations.
if (m_Connection.FSW_SenProc_ADSensor_ptr->GetIsValid()) // Star Tracker has a new measurement
{
QUATERNION str_Q_CBI2BDY = m_Connection.FSW_SenProc_ADSensor_ptr->GetSensedQuaternion_CBI2BDY();
double str_CurrentTime_s = m_Connection.FSW_SenProc_ADSensor_ptr->GetLastValidTime_SCLK_s();
double thisDeltaTime_s = str_CurrentTime_s - m_PrevTime_s;
// If we have a new measurement that is within a parameterized time of an old good measurement, compute the quaternion difference and get the rate.
if (thisDeltaTime_s < m_Parm.D_MaxDeltaTimeForRate_s && thisDeltaTime_s > EPSILON)
{
QUATERNION qDelta;
Math_Qinv_Q_Mult(m_PrevQ_CBI2BDY, str_Q_CBI2BDY, qDelta);
// Calculate Delta Angles using small-angle approximation
VECTOR deltaAng_rd = 2.0 * Math_Sign(qDelta[3]) * VECTOR(qDelta[0], qDelta[1], qDelta[2]);
// Determine Rate by back-differencing the quaternion
m_Tlm.Rate_BDY_rdps = deltaAng_rd / thisDeltaTime_s;
}
// Setup the persistence variables for the next back-difference
m_PrevTime_s = str_CurrentTime_s;
m_PrevQ_CBI2BDY = str_Q_CBI2BDY;
}
break;
}
return Ret;
}
Step 4: Create, Connect, and Configure the Virtual IMU
To configure our simulation to use our new Virtual_IMU, we use the Configuration Manager.
Create
Create a Copy of our SOLIS-Generated Configuration Set, named WithVirtual_IMU
Create an instance of our Virtual_IMU component, configured as-shown
- Priority 315 defines when this component executes, relative to other priorities. We place it between FSW_SenProc_IMU (310) and FSW_AttDetermination (600). The Configuration Overview button provides a table of all Components and their priorities.
- Master ID 315 defines the command and telemetry APID (Application ID). This must be unique from other APIDs
- Be sure the Output File goes into
FSW_Components.cfg
. An error would occur if this goes into theODY_*
cfg files
Connect
Add (or edit) Connections to the Virtual_IMU
- We want the Virtual_IMU to get data from the FSW_SenProc_IMU and FSW_SenProc_STR_Sinclair_STR.
Similar to the Connections section, the Parameters can be defined
- If not specified, they will default to the DefaultValue specified in the XML.
- If the XML did not specify a DefaultValue, the value will be zero.
Configure
Since we need to modify the Attitude Determination, create a Copy of the FSW_AttDetermination
- The default copy parameters are good, just change the Version Name to WithVirtual_IMU.
Now we need to modify the Connection_FSW_SenProc_RTE
- Select the 2nd row in the Connections section.
- Click the edit icon.
- Change the Connected Component to Virtual_IMU.
- Change the Accessor to FSW_SenProc_RTE.
- Not Connection_FSW_SenProc_RTE, as this is the accessor to the input IMU.
Select WithVirtual_IMU as the Active Configuration Set
- This tells SOLIS to write the Configuration files as you’ve set them up within that set, with our new Virtual IMU.
Add the new Custom/Virtual_IMU packet to the recorded telemetry
Running the Simulation
At this point the system is fully-configured to run the new logic.
Simulation
- Start the simulation running 10x realtime
- Using the Real-Time Commanding feature, issue our new Custom command to change the Rate Source
- Next, issue a Slew Relative command to cause the Star Tracker to point toward the Earth
- Lastly, run the GoTo Hold command timed while the Star Tracker Field of View is not intersecting Earth
Telemetry Results
Telemetry results can be seen using the STK Data Provider. All SOLIS data will be under the User Supplied Data section, with our new Virtual IMU telemetry under the Custom folder.
Below shows the results of this simulation. At ~152 seconds the command was issued to start using the Virtual_IMU rate solution. This held together until the slew was commanded that now blocked the star tracker. The system ultimately recovered after the GoTo Hold command was issued at ~450 sec.
Possible things to try from here
- Have your Virtual_IMU DLL compiled for Debug
- While the simulation is running, attach the Debugger to MAX.exe
- Set breakpoints and step through the logic
- Design logic that is more robust to star tracker outages, such as implementation of feed-forward propagation on expected torques
- Add an additional Star Tracker, and associated logic
- Create a fault within the IMU (modify a parameter on the OSC_SenMdl_IMU using
MISC_CONFIG_PARSE
)- Fault example
MISC_CONFIG_PARSE(“OSC_SenMdl_IMU_MPU.B_IsOn = 0;”)
- Create a telemetry monitor that can detect this
- Create an automated response that switches the system to use the Virtual
- Try to resolve a robust solution to get the system safely pointed at the Sun
- Fault example
- Create telemetry that compares the IMU to the Virtual IMU
- Modify the timeout parameters
- Start working an alternate customization idea…